/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'dart:convert';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:hadss_avoid_area/hadss_avoid_area.dart';

class AvoidAreaManager {
  AvoidArea? avoidAreaSystem;
  AvoidArea? avoidAreaCutout;
  AvoidArea? avoidAreaKeyboard;
  AvoidArea? avoidAreaNavigationIndicator;
  AvoidArea? avoidAreaSystemGesture;
  AvoidAreaState avoidAreaState = AvoidAreaState();
  var padding = EdgeInsets.zero;
  MediaQueryData? mediaQuery;
  List<ValueChanged<AvoidAreaState>> listeners = [];

  static final AvoidAreaManager _instance = AvoidAreaManager._internal();

  factory AvoidAreaManager() {
    return _instance;
  }

  AvoidAreaManager._internal() {
    initAvoidArea();
    initAvoidAreaListener();
  }

  updateMediaQuery(BuildContext context) {
    mediaQuery = MediaQuery.maybeOf(context);
    ///Android addAvoidAreaListener未实现，只能主动获取刷新
    bool isAndroid = Theme.of(context).platform == TargetPlatform.android;
    if (isAndroid) {
      initAvoidArea();
    } else {
      update();
    }
  }

  Future<void> initAvoidArea() async {
    avoidAreaSystem =
        await AvoidAreaApi.getWindowAvoidArea(AvoidAreaType.system);
    avoidAreaCutout =
        await AvoidAreaApi.getWindowAvoidArea(AvoidAreaType.cutout);
    avoidAreaKeyboard =
        await AvoidAreaApi.getWindowAvoidArea(AvoidAreaType.keyboard);
    avoidAreaNavigationIndicator = await AvoidAreaApi.getWindowAvoidArea(
        AvoidAreaType.navigationIndicator);
    avoidAreaSystemGesture =
        await AvoidAreaApi.getWindowAvoidArea(AvoidAreaType.systemGesture);
    update();
  }

  void initAvoidAreaListener(){
    AvoidAreaApi.addAvoidAreaListener((event) {
      var result = json.decode(event);
      AvoidArea avoidArea = AvoidArea.fromJson(result["area"]);
      switch(AvoidAreaType.values[result["type"]]){
        case AvoidAreaType.system:
          avoidAreaSystem = avoidArea;
          break;
        case AvoidAreaType.cutout:
          avoidAreaCutout = avoidArea;
          break;
        case AvoidAreaType.keyboard:
          avoidAreaKeyboard = avoidArea;
          break;
        case AvoidAreaType.systemGesture:
          avoidAreaSystemGesture = avoidArea;
          break;
        case AvoidAreaType.navigationIndicator:
          avoidAreaNavigationIndicator = avoidArea;
          break;
      }
      update();
    });
  }

  void update() {
    if (mediaQuery == null) {
      return;
    }
    List<AvoidArea?> avoidAreaList = [
      avoidAreaSystem,
      avoidAreaKeyboard,
      avoidAreaNavigationIndicator,
      avoidAreaSystemGesture
    ];
    var left = 0.0;
    var top = 0.0;
    var right = 0.0;
    var bottom = 0.0;
    for (var avoidArea in avoidAreaList) {
      if (avoidArea != null) {
        if (avoidArea.leftRect.width > left) {
          left = avoidArea.leftRect.width / mediaQuery!.devicePixelRatio;
        }
        if (avoidArea.topRect.height > top) {
          top = avoidArea.topRect.height / mediaQuery!.devicePixelRatio;
        }
        if (avoidArea.rightRect.width > right) {
          right = avoidArea.rightRect.width / mediaQuery!.devicePixelRatio;
        }
        if (avoidArea.bottomRect.height > bottom) {
          bottom = avoidArea.bottomRect.height / mediaQuery!.devicePixelRatio;
        }
      }
    }
    padding = EdgeInsets.fromLTRB(left, top, right, bottom);
    initAreaCutout();
  }

  void initAreaCutout() {
    CutoutPosition cutoutPosition = CutoutPosition.unknown;
    var rect = ui.Rect.zero;
    if (mediaQuery != null && avoidAreaCutout != null) {
      final AvoidArea avoidArea = avoidAreaCutout!;
      double screenWidth = getScreenWidth();
      double screenHeight = getScreenHeight();
      if (avoidArea.topRect.width != 0 && avoidArea.topRect.height != 0) {
        rect = createRect(avoidArea.topRect);
        double offset = rect.center.dx - screenWidth / 2.0;
        cutoutPosition = calculateCutoutPosition(offset, CutoutPosition.topLeft,
            CutoutPosition.topRight, CutoutPosition.topCenter);
      } else if (avoidArea.bottomRect.width != 0 &&
          avoidArea.bottomRect.height != 0) {
        rect = createRect(avoidArea.bottomRect);
        double offset = rect.center.dx - screenWidth / 2.0;
        cutoutPosition = calculateCutoutPosition(offset, CutoutPosition.bottomLeft,
            CutoutPosition.bottomRight, CutoutPosition.bottomCenter);
      } else if (avoidArea.leftRect.width != 0 &&
          avoidArea.leftRect.height != 0) {
        rect = createRect(avoidArea.leftRect);
        double offset = rect.center.dy - screenHeight / 2.0;
        cutoutPosition = calculateCutoutPosition(offset, CutoutPosition.topLeft,
            CutoutPosition.bottomLeft, CutoutPosition.left);
      } else if (avoidArea.rightRect.width != 0 &&
          avoidArea.rightRect.height != 0) {
        rect = createRect(avoidArea.rightRect);
        double offset = rect.center.dy - screenHeight / 2.0;
        cutoutPosition = calculateCutoutPosition(offset, CutoutPosition.topRight,
            CutoutPosition.bottomRight, CutoutPosition.right);
      }
    }
    _notifyListeners(AvoidAreaState(padding: padding,cutoutPosition: cutoutPosition,rect: rect));
  }

  ui.Rect createRect(Rect sourceRect) {
    return ui.Rect.fromLTWH(
        sourceRect.left / mediaQuery!.devicePixelRatio,
        sourceRect.top / mediaQuery!.devicePixelRatio,
        sourceRect.width / mediaQuery!.devicePixelRatio,
        sourceRect.height / mediaQuery!.devicePixelRatio);
  }

  CutoutPosition calculateCutoutPosition(double offset,CutoutPosition minPosition,CutoutPosition maxPosition,CutoutPosition center){
    const maxOffset = 5;
    const minOffset = -5;
    return offset > maxOffset
        ? maxPosition
        : (offset < minOffset
        ? minPosition
        : center);
  }

  bool isTablet() {
    if (mediaQuery == null) {
      return false;
    }
    final aspectRatio = getFixedAspectRatio();
    return aspectRatio < 1.6;
  }

  double getFixedAspectRatio() {
    if (mediaQuery == null) return 1.0;
    final size = mediaQuery!.size;
    final double longSide = size.longestSide;
    final double shortSide = size.shortestSide;

    return longSide / shortSide;
  }

  bool isLandscape() {
    if (mediaQuery == null) {
      return false;
    }
    return mediaQuery!.orientation == Orientation.landscape;
  }

  double getScreenWidth() {
    return mediaQuery?.size.width ?? 0;
  }

  double getScreenHeight() {
    return mediaQuery?.size.height ?? 0;
  }

  void addListener(ValueChanged<AvoidAreaState> listener) {
    listener(avoidAreaState);
    listeners.add(listener);
  }

  void removeListener(ValueChanged<AvoidAreaState> listener) {
    listeners.remove(listener);
  }

  void _notifyListeners(AvoidAreaState newAvoidAreaState) {
    if (avoidAreaState != newAvoidAreaState) {
      if (avoidAreaState.rect == newAvoidAreaState.rect &&
          avoidAreaState.padding == newAvoidAreaState.padding &&
          avoidAreaState.cutoutPosition == newAvoidAreaState.cutoutPosition) {
        return;
      }
      avoidAreaState = newAvoidAreaState;
      for (var listener in listeners) {
        listener(avoidAreaState);
      }
    }
  }
}

enum CutoutPosition {
  topLeft,
  topCenter,
  topRight,
  right,
  bottomLeft,
  bottomCenter,
  bottomRight,
  left,
  unknown,
}

class AvoidAreaState {
  EdgeInsets padding;
  CutoutPosition cutoutPosition;
  ui.Rect rect;

  AvoidAreaState(
      {this.padding = EdgeInsets.zero,
      this.cutoutPosition = CutoutPosition.unknown,
      this.rect = ui.Rect.zero});
}
